package dnl.jexem.prcs;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang.StringUtils;

import dnl.jexem.cmd.CommandExecution;
import dnl.jexem.cmd.CommandExecutionFactory;

/**
 * 
 * @author Daniel Orr
 * 
 */
public class UnxCommandsProcessOperations implements ProcessOperations {

	private static Pattern psLinePattern = Pattern
			.compile("(\\w+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d{1,2})\\s+(\\w+|\\d{2}:\\d{2})\\s+(\\u003F?|[\\w/\\d]+)\\s+(\\d{2}:\\d{2}:\\d{2})\\s+(.*)");

	@Override
	public ProcessInfo getProcess(int pid) {
		CommandExecution commandExecution = CommandExecutionFactory.createLocalCommandExecution(
				"ps", "-f", "-p", Integer.toString(pid));
		commandExecution.executeCommand(5000);
		// if (commandExecution.getExitStatus() == 1) {
		// // when ps does not find a process with the given id it simply exits
		// with exit status 1
		// }
		// else if (commandExecution.getExitStatus() != 0) {
		// System.err.println("Error executing'"
		// + commandExecution.getFullCommand()+"'");
		// System.err.println("output"+commandExecution.getOutput());
		// System.err.println("error output:\n"+commandExecution.getErrorOutput());
		// return null;
		// }
		List<ProcessInfo> ps = extractProcessInfoFromPsOutput(commandExecution, true);
		if (ps.isEmpty())
			return null;
		return ps.get(0);
	}

	@Override
	public List<ProcessInfo> getProcesses() {
		return getProcessesImpl(true);
	}
	
	public List<ProcessInfo> getNonSystemProcesses() {
		return getProcessesImpl(false);
	}
	
	public List<ProcessInfo> getProcessesImpl(boolean withSystemProcs) {
		CommandExecution commandExecution = CommandExecutionFactory.createLocalCommandExecution(
				"ps", "-aef");
		commandExecution.executeCommand(10000);
		if (commandExecution.getExitStatus() != 0) {
			System.err.println("Error executing: " + commandExecution.getFullCommand());
			return Collections.emptyList();
		}
		return extractProcessInfoFromPsOutput(commandExecution, withSystemProcs);
	}

	private List<ProcessInfo> extractProcessInfoFromPsOutput(CommandExecution commandExecution,
			boolean addSystemProcesses) {
		String output = commandExecution.getOutput();
		String[] lines = StringUtils.split(output, "\n\r");
		List<ProcessInfo> processes = new ArrayList<ProcessInfo>();
		for (int i = 1; i < lines.length; i++) {
			ProcessInfo pi = parsePsLine(lines[i]);
			if(addSystemProcesses){
				processes.add(pi);
			}
			else if(!(pi.getProcessID() < 11 ||pi.getParentProcessID() <11)){
				processes.add(pi);
			}
		}

		return processes;
	}

	@Override
	public List<ProcessInfo> grepProcesses(String grepExpression) {
		CommandExecution commandExecution = CommandExecutionFactory.createLocalCommandExecution(
				"pgrep", "-f", grepExpression);
		commandExecution.executeCommand(1000);
		String output = commandExecution.getOutput();
		String[] pidsStr = StringUtils.split(output, "\n\r \t");
		List<ProcessInfo> result = new ArrayList<ProcessInfo>();
		for (String pid : pidsStr) {
			ProcessInfo process = getProcess(Integer.parseInt(pid));
			if (process != null) {
				result.add(process);
			}
		}
		return result;
	}

	private ProcessInfo parsePsLine(String line) {

		Matcher matcher = psLinePattern.matcher(line);
		if (matcher.matches()) {
			ProcessInfo pi = new ProcessInfo();
			pi.setUserID(matcher.group(1));
			pi.setProcessID(Integer.parseInt(matcher.group(2)));
			pi.setParentProcessID(Integer.parseInt(matcher.group(3)));
			pi.setCpuUsagePercent(Integer.parseInt(matcher.group(4)));
			pi.setStartTime(matcher.group(5));
			pi.setCmd(matcher.group(8));
			return pi;
		} else {
			System.err.println("could not match: " + line);
			return null;
		}
	}

	@Override
	public boolean killProcess(int pid) {
		CommandExecution commandExecution = CommandExecutionFactory.createLocalCommandExecution(
				"kill", Integer.toString(pid));
		commandExecution.executeCommand(1000);
		return true;
	}

}
